/*
 * Team members: Fabio Canache    ID: 564693
 *               Denise Videtta   ID: 565525
 * Date:         10/06/2008
 * 
 * Project #1 - Question: 2a GoL Version 2
 * 
 * ALG 2008 Fall
 * Instructor: Dr. Oleg Komogortsev, TSU
 * 
 */

package gameoflife;
import java.util.Vector;

/**
 *  This class implement the Game of Life Version 2  
 * 
 */
public class GameOfLifeVersion2 extends Thread {
    
    private lifequeen.LifeQueen lifeQueen;

    private Vector<Cell> borns = new Vector<Cell>();
    private Vector<Cell> deaths = new Vector<Cell>();
    private Vector<Cell> mayLive = new Vector<Cell>();
    private Vector<Cell> mayDie = new Vector<Cell>();
    
    final boolean ALIVE = true;
    final boolean DEAD = false;
    
    private int maxRow;
    private int maxColumn;
    private int neighbors[][];
    private boolean map[][];
    private int conf[][];
    
    private int numberDeads;
    private int numberBorns;
    private int numberMayLive;
    private int numberMayDie;
    private int generationCounter;
    
    public GameOfLifeVersion2 (lifequeen.LifeQueen lifeQueen) {
        this.lifeQueen = lifeQueen;
    }
    
    @Override
    public void run () {
        this.startGameOfLifeVersion2();
    }
    
    /**
     *  Starts the Game of Life Version 2 process until the user does not want
     *  more generations
     */
    private void startGameOfLifeVersion2() {

        generationCounter = 0;
        
        maxRow = this.lifeQueen.getGoLNBoardSize();
        maxColumn = this.lifeQueen.getGoLMBoardSize();
        neighbors= new int[maxRow + 2][maxColumn + 2];
        map = new boolean[maxRow + 2][maxColumn + 2];
        initialize();

        try {
            
            do {
                
                generationCounter++;
                
                vivify();
                kill();
                mayLive = new Vector<Cell>();
                mayDie = new Vector<Cell>();

                addNeighbors();
                subNeighbors();
                
                printMap();
                
                borns = new Vector<Cell>();
                deaths = new Vector<Cell>();

                numberMayDie = getSize(mayDie);
                numberMayLive = getSize(mayLive);
                
                lifeQueen.setGoLNewBorns(Integer.toString(this.numberBorns));
                lifeQueen.setGoLNewDeads(Integer.toString(this.numberDeads));
                lifeQueen.setGoLMayLive(Integer.toString(this.numberMayLive));
                lifeQueen.setGoLMayDie(Integer.toString(this.numberMayDie));
                lifeQueen.setGoLDescriptionArea("This is the generation number: " + this.generationCounter + "\nTotal number of changes: " + (this.numberBorns + this.numberDeads));
                
                this.numberDeads = 0;
                this.numberBorns = 0;
                this.numberMayDie = 0;
                this.numberMayLive = 0;
                
                lifeQueen.setContinueToNextGeneration(true);
                while (lifeQueen.getContinueToNextGeneration()) {
                
                }
                
            } while (lifeQueen.getExecuteGoL());


        } catch (Exception e) {
            
        }
    }

    /**
     *  Update the count of neightbors of each cell that is surrounding a 
     *  recently alive cell. And is also update the lists MayDie and
     *  MayLive for the next generation
     */
    public void addNeighbors() {

        for (int k = 0; k < borns.size(); k++) {

            Cell cell = (Cell) borns.elementAt(k);
            
            for (int i = cell.getRow() - 1; i <= cell.getRow() + 1; i++) {
                for (int j = cell.getCol() - 1; j <= cell.getCol() + 1; j++) {
                    
                    if (i != cell.getRow() || j != cell.getCol()) {
                        neighbors[i][j]++;
                        switch (neighbors[i][j]) {
                            case 0:
                            case 1:
                                if (map[i][j] == ALIVE) {
                                    mayDie.add(new Cell(i, j));
                                }
                                break;
                            case 3:
                                if (map[i][j] == DEAD) {
                                    mayLive.addElement(new Cell(i, j));
                                }
                                break;
                            case 4:
                            case 5:
                            case 6:
                            case 7:
                            case 8:
                                if (map[i][j] == ALIVE) {
                                    mayDie.add(new Cell(i, j));
                                }
                                break;
                        }
                    }
                }
            }
            
        }

    }

    /**
     *  Update the count of neightbors of each cell that is surrounding a 
     *  recently death cell. And is also update the lists MayDie and
     *  MayLive for the next generation
     */
    public void subNeighbors() {

        for (int k = 0; k < deaths.size(); k++) {

            Cell cell = (Cell) deaths.elementAt(k);
            for (int i = cell.getRow() - 1; i <= cell.getRow() + 1; i++) {

                for (int j = cell.getCol() - 1; j <= cell.getCol() + 1; j++) {

                    if (i != cell.getRow() || j != cell.getCol()) {
                        neighbors[i][j]--;
                        switch (neighbors[i][j]) {
                            case 0:
                            case 1:
                                if (map[i][j] == ALIVE) {
                                    mayDie.add(new Cell(i, j));
                                }
                                break;
                            case 3:
                                if (map[i][j] == DEAD) {
                                    mayLive.addElement(new Cell(i, j));
                                }
                                break;
                            case 4:
                            case 5:
                            case 6:
                            case 7:
                            case 8:
                                if (map[i][j] == ALIVE) {
                                    mayDie.add(new Cell(i, j));
                                }
                                break;
                        }
                    }
                }
            }
        }
    }

    /**
     *  Transforms the cells that are in MayLive list and have the corresponding
     *  count of neighbors in "Alive" cells and add them into the borns list
     */

    public void vivify() {

        for (int i = 0; i < mayLive.size(); i++) {

            Cell cell = (Cell) mayLive.elementAt(i);
            int row = cell.getRow();
            int col = cell.getCol();
            int count = neighbors[row][col];
            if (map[row][col] == DEAD &&
                    count == 3 &&
                    row >= 1 && row <= maxRow &&
                    col >= 1 && col <= maxColumn) {

                map[row][col] = ALIVE;
                numberBorns++;
                borns.add(cell);

            }

        }
    }

    /**
     *  Transforms the cells that are in MayDie list and have the corresponding
     *  count of neighbors in "Dead" cells and add them into the deaths list
     */
    public void kill() {

        for (int i = 0; i < mayDie.size(); i++) {

            Cell cell = (Cell) mayDie.elementAt(i);
            int row = cell.getRow();
            int col = cell.getCol();
            int count = neighbors[row][col];
            if (map[row][col] == ALIVE &&
                    row >= 1 && row <= maxRow &&
                    col >= 1 && col <= maxColumn &&
                    (count == 1 || count == 4 || count == 5 ||
                    count == 6 || count == 7 || count == 8 || count == 0)) {
                map[row][col] = DEAD;
                numberDeads++;
                deaths.add(cell);
            }
        }
    }

    /**
     *  Initialize the first configuration for the board with the
     *  user entries for "Alive" cells. Including the correspondent
     *  lists MayDie and MayLive
     */
    public void initialize() {

        /* Initialization of board just lives and deaths*/
        conf = lifeQueen.getGoLBoardConfiguration();
        
        for (int i = 0; i <= maxRow+1; i++) {
            for (int j = 0; j <= maxColumn+1; j++) {
                /* If is in the file*/
                if (conf[i][j] == 1) {
                    map[i][j] = ALIVE;
                } else {
                    map[i][j] = DEAD;
                }
            }
        }

        /* Initialization of the neighbors count*/
        initializeNeighbors();

        /* Initialization of mayDie and mayDie lists*/

        for (int i = 1; i <= maxRow; i++) {

            for (int j = 1; j <= maxColumn; j++) {
                int count = neighbors[i][j];

                switch (count) {
                    case 0:
                    case 1:
                        if (map[i][j] == ALIVE) {
                            mayDie.add(new Cell(i, j));
                        }
                        break;
                    case 3:
                        if (map[i][j] == DEAD) {
                            mayLive.addElement(new Cell(i, j));
                        }
                        break;
                    case 4:
                    case 5:
                    case 6:
                    case 7:
                    case 8:
                        if (map[i][j] == ALIVE) {
                            mayDie.add(new Cell(i, j));
                        }
                        break;
                }
            }

        }

    }

    /**
     *  Initialize the first configuration for the neighnors in the board 
     */
    public void initializeNeighbors() {

        for (int i = 1; i <= maxRow; i++) {

            for (int j = 1; j <= maxColumn; j++) {

                neighbors[i][j] = countNeighbors(i, j);
            }
        }

    }

    /**
     *  Return the number of neighbors for a specific cell
     */
    public int countNeighbors(int row, int col) {

        int numberNeighbors = 0;

        for (int i = row - 1; i <= row + 1; i++) {

            for (int j = col - 1; j <= col + 1; j++) {

                if (map[i][j] == ALIVE) {
                    numberNeighbors++;
                }
            }

        }
        if (map[row][col] == ALIVE) {
            numberNeighbors--;
        }
        return numberNeighbors;
    }

    /**
     *  Print the current generation in the board
     */
    public void printMap() {

        for (int i = 1; i <= maxRow; i++) {

            for (int j = 1; j <= maxColumn; j++) {

                if (map[i][j] == ALIVE) {
                    lifeQueen.placeAlive((i - 1), (j - 1));
                } else {
                    lifeQueen.removeAlive((i - 1), (j - 1));
                }
            }
 
        }
    }

    /**
     *  Return the size of the Vector of cells without repetitions
     */
      public static int getSize(Vector<Cell> vector){
       
        Vector<Cell> temp = new Vector<Cell>();
        boolean contains = false;
        for (int i = 0; i < vector.size(); i++) {
            Cell cell = (Cell) vector.elementAt(i);
            if(temp.size() == 0){
                temp.add(cell);
            }
           
            for (int j = 0; j < temp.size(); j++) {
                Cell cellTemp = (Cell) temp.elementAt(j);
                if(cell.getCol() == cellTemp.getCol() && cell.getRow() == cellTemp.getRow()){
                    contains = true;
                    break;
                }else{
                    contains = false;
                }      
            }
            if(!contains){
                temp.add(cell);
            }
        }
        return temp.size();
   
    }

}
